package com.loie.xcx.aspect;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.loie.xcx.comm.CustomException;
import com.loie.xcx.comm.JsonResult;
import com.loie.xcx.service.SystemService;
import com.loie.xcx.util.SecureUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import static java.lang.Math.abs;

/**
 * @author zhl
 */
@Aspect
@Component
@Slf4j
@AllArgsConstructor
public class SignatureValidation {

    /**
     * 时间戳请求最小限制(600s)
     * 设置的越小，安全系数越高，但是要注意一定的容错性
     */
    private static final long MAX_REQUEST = 10 * 60 * 1000L;

    private static final int NONCE_SIZE = 16;
    private final RedisTemplate<String, String> redisTemplate;
    private final SystemService systemService;

    /**
     * 验签切点(完整的找到设置的文件地址)
     */
    @Pointcut("execution(@com.loie.xcx.aop.SignatureValidation * *(..))")
    private void verifyUserKey() {
    }

    private boolean checkNonce(String key, String xcxNonce) {
        if (StringUtils.isNotBlank(xcxNonce) && xcxNonce.length() >= NONCE_SIZE) {
            String redisKey = String.format("xcxAPI:xcxNonce:%s:%s", key, xcxNonce);
            Boolean ind = redisTemplate.hasKey(redisKey);
            if (Boolean.FALSE.equals(ind) || ind == null) {
                redisTemplate.opsForValue().set(redisKey, "", 1, TimeUnit.DAYS);
                return true;
            }
        }
        return false;
    }

    private String getAppSecret(String xcxAppKey) {
        //获取秘钥
        return systemService.getSecretByAppId(xcxAppKey);
    }

    /**
     * 获取请求数据，并校验签名数据
     */
    @Around("verifyUserKey()")
    public Object doBasicProfiling(ProceedingJoinPoint point) throws Throwable {

        final String classType = "java.util.ArrayList";
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();

        String xcxAppKey = request.getHeader("Xcx-AppKey");
        String xcxTimestamp = request.getHeader("Xcx-Timestamp");
        String xcxNonce = request.getHeader("Xcx-Nonce");
        String xcxSignature = request.getHeader("Xcx-Signature");
        if (StringUtils.isNotBlank(xcxTimestamp) && abs(System.currentTimeMillis() - Long.parseLong(xcxTimestamp)) < MAX_REQUEST) {
            if (checkNonce(xcxAppKey, xcxNonce)) {
                String appSecret = getAppSecret(xcxAppKey);
                if (StringUtils.isNotBlank(xcxAppKey) && StringUtils.isNotBlank(appSecret)) {
                    if (StringUtils.isNotBlank(xcxSignature)) {
                        //Xcx_Timestamp+Xcx_Nonce+Xcx_AppKey+appSecret+JSON BODY
                        StringBuilder str = new StringBuilder();
                        Object[] obj = point.getArgs();
                        str.append(xcxTimestamp).append(xcxNonce).append(xcxAppKey).append(appSecret).append(obj[0]);
                        String md5 = SecureUtil.MD5Encode(str.toString());
                        if (xcxSignature.equals(md5)) {
                            //获取代理方法的参数
                            Signature signature = point.getSignature();
                            MethodSignature methodSignature = (MethodSignature) signature;
                            //获取参数名
                            String[] argsName = methodSignature.getParameterNames();
                            if (null != argsName) {
                                final String paramKey = "jsonStr";
                                if (paramKey.equalsIgnoreCase(argsName[0])) {
                                    //解密，返回明文
                                    obj[0] = SecureUtil.decryptAES((String) obj[0], appSecret);
                                    log.info("入参==>" + obj[0] + ", Xcx-Nonce==>" + xcxNonce);
                                    Object result = point.proceed(obj);
                                    //处理加密返回
                                    JSONObject jsonObject = JSONObject.from(result);
                                    log.info("出参==>" + jsonObject.toJSONString() + ", Xcx-Nonce==>" + xcxNonce);
                                    Object data = jsonObject.get("data");
                                    if (data == null) {
                                        return JSONObject.parseObject(jsonObject.toJSONString(), JsonResult.class);
                                    } else if (classType.equals(data.getClass().getTypeName())) {
                                        jsonObject.put("data", SecureUtil.encryptAES(JSONArray.from(data).toJSONString(), appSecret));
                                        return JSONObject.parseObject(jsonObject.toJSONString(), JsonResult.class);
                                    }
                                    jsonObject.put("data", SecureUtil.encryptAES(JSONObject.from(data).toJSONString(), appSecret));
                                    return JSONObject.parseObject(jsonObject.toJSONString(), JsonResult.class);
                                }
                            }
                        } else {
                            log.info("验签内容：" + str);
                        }
                    }
                }
            }
        }
        throw new CustomException(400, "参数异常");
    }
}